#include "StdAfx.h"
#include "Line.h"
#include "Renderer.h"

CLine::CLine(void)
{
}


CLine::~CLine(void)
{
}

void CLine::Transform(const CMatrix4& matTransform)
{
	for (int i = 0; i < 2; ++i)
	{
		m_vertex[i].transformedPosition = matTransform * m_vertex[i].worldPosition;
	}
}

bool moveCondition(bool bPositiveDir, DATA_TYPE v, DATA_TYPE vCheckTarget)
{
	if (bPositiveDir)
	{
		return v < ceilf(vCheckTarget);
	}
	else
	{
		return v > floorf(vCheckTarget);
	}
}

void CLine::Rasterize(CRenderer* pRenderer)
{
	vector<CVector4>& vColors = pRenderer->GetColors();

	DATA_TYPE x0 = m_vertex[0].transformedPosition.x;
	DATA_TYPE y0 = m_vertex[0].transformedPosition.y;
	DATA_TYPE x1 = m_vertex[1].transformedPosition.x;
	DATA_TYPE y1 = m_vertex[1].transformedPosition.y;

	bool bPositiveX = (x1 - x0) > 0;
	bool bPositiveY = (y1 - y0) > 0;

	// clipping
	DATA_TYPE minX = __min(x0, x1);
	DATA_TYPE maxX = __max(x0, x1);
	DATA_TYPE minY = __min(y0, y1);
	DATA_TYPE maxY = __max(y0, y1);
	if (minX > WINDOW_WIDTH || maxX < 0 || minY > WINDOW_HEIGHT || maxY < 0)
		return;

	// y = mx + b
	DATA_TYPE m = (y1 - y0) / (x1 - x0);
	DATA_TYPE b = y0 - m * x0;
	if (x1 > x0)
	{
		if (x0 < 0)
		{
			x0 = 0;
			y0 = m * x0 + b;
			if (y0 > WINDOW_HEIGHT)
				return;
		}
		if (x1 > WINDOW_HEIGHT)
		{
			x1 = WINDOW_HEIGHT;
			y1 = m * x1 + b;
			if (y1 < 0)
				return;
		}
	}
	else if(x1 < x0)
	{
		if (x1 < 0)
		{
			x1 = 0;
			y1 = m * x1 + b;
			if (y1 > WINDOW_HEIGHT)
				return;
		}
		if (x0 > WINDOW_HEIGHT)
		{
			x0 = WINDOW_HEIGHT;
			y0 = m * x0 + b;
			if (y0 < 0)
				return;
		}
	}
	if (y1 > y0)
	{
		if (y0 < 0)
		{
			y0 = 0;
			x0 = (y0 - b) / m;
			if (x0 > WINDOW_WIDTH)
				return;
		}
		if (y1 > WINDOW_WIDTH)
		{
			y1 = WINDOW_WIDTH;
			x1 = (y1 - b) / m;
			if (x1 < 0)
				return;
		}
	}
	else if(y1 < y0)
	{
		if (y1 < 0)
		{
			y1 = 0;
			x1 = (y1 - b) / m;
			if (x1 > WINDOW_WIDTH)
				return;
		}
		if (y0 > WINDOW_WIDTH)
		{
			y0 = WINDOW_WIDTH;
			x0 = (y0 - b) / m;
			if (x0 < 0)
				return;
		}
	}
	
	if (fabs(x1 - x0) > fabs(y1 - y0))
	{
		// TODO: ceil? floor?
		DATA_TYPE x = bPositiveX ? floorf(x0) : ceilf(x0);
		DATA_TYPE y = Round(m * x + b);
		DATA_TYPE d = m * x + b - y;
		while (moveCondition(bPositiveX, x, x1))
		{
			if (d > 0.5f)
			{
				y += 1;
				d -= 1;
			}
			else if (d < -0.5f)
			{
				y -= 1;
				d += 1;
			}
			else
			{
				x += bPositiveX ? 1 : -1;
				d += bPositiveX ? m : -m;
			}

			if (-0.5f < d && d < 0.5f)
			{
				int idx = (int)x + (int)y * WINDOW_WIDTH;
				CVector4 v(x, y, 0.f, 1.f);
  				DATA_TYPE a = CVector4::GetDistance(v, m_vertex[0].transformedPosition) / 
							  CVector4::GetDistance(m_vertex[1].transformedPosition, m_vertex[0].transformedPosition);
				a = clamp(a, 0.f, 1.f);
				vColors.at(idx) = CVector4::Interpolate(a, m_vertex[0].color, m_vertex[1].color);
			}
		}
	}
	else
	{
		// x = my + b
		DATA_TYPE m = (x1 - x0) / (y1 - y0);
		DATA_TYPE b = x0 - m * y0;

		DATA_TYPE y = bPositiveY ? floorf(y0) : ceilf(y0);
		DATA_TYPE x = Round(m * y + b);
		DATA_TYPE d = m * y + b - x;
		while (moveCondition(bPositiveY, y, y1))
		{
			if (d > 0.5f)
			{
				x += 1;
				d -= 1;
			}
			else if (d < -0.5f)
			{
				x -= 1;
				d += 1;
			}
			else
			{
				y += bPositiveY ? 1 : -1;
				d += bPositiveY ? m : -m;
			}
			if (-0.5f < d && d < 0.5f)
			{
				int idx = (int)x + (int)y * WINDOW_WIDTH;
				CVector4 v(x, y, 0.f, 1.f);
				DATA_TYPE a = CVector4::GetDistance(v, m_vertex[0].transformedPosition) / 
							  CVector4::GetDistance(m_vertex[1].transformedPosition, m_vertex[0].transformedPosition);
				a = clamp(a, 0.f, 1.f);
				vColors.at(idx) = CVector4::Interpolate(a, m_vertex[0].color, m_vertex[1].color);

				//cout << "pixel at " << x << ',' << y << endl;
			}
		}
	}
}

